// 版权归 瞄你个汪 所有。使用本代码应遵守相关法律法规和Apache 2.0开源许可要求。
package top.geeke.handler;

import cn.hutool.jwt.JWTPayload;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import top.geeke.config.SysUserSessionManager;
import top.geeke.annotation.AllowAnonymous;
import top.geeke.constants.AppConstPool;
import top.geeke.mapper.SysMenuMapper;
import top.geeke.toolkit.JwtUtils;
import top.geeke.eunm.SysErrorEnum;
import top.geeke.toolkit.RequestUtils;
import top.geeke.toolkit.http.AppException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * 权限拦截器
 * </p>
 * @author 瞄你个汪
 * @since 2024/6/7 22:14
 */
@Slf4j
@Component
public class AuthorizeInterceptor implements HandlerInterceptor {
    @Autowired
    private SysUserSessionManager sessionManager;
    @Autowired
    private SysMenuMapper sysMenuMapper;
    @Autowired
    private JwtUtils jwtUtils;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();

            // 放行匿名接口
            if (method.isAnnotationPresent(AllowAnonymous.class)) return true;

            // 校验token
            String token = RequestUtils.getToken();
            if (Strings.isBlank(token) || !jwtUtils.verify(token)) throw new AppException(SysErrorEnum.R401);

            // 获取并设置userId
            String jwtId = jwtUtils.getJwtIdFromToken(token);
            if (jwtId == null) throw new AppException(SysErrorEnum.R401);

            // 将用户id/租户id设置到请求体
            // 必须在sessionManager前设置，否则会抛异常
            Long[] ids = Arrays.stream(jwtId.split("-")).map(Long::parseLong).toArray(Long[]::new);
            request.setAttribute(JWTPayload.JWT_ID, ids[1]);
            request.setAttribute(AppConstPool.TENANT_ID, ids[0]);

            // 放行超级管理员
            if (sessionManager.isSuperAdmin()) return true;

            // 若接口地址为 /api/前缀，则校验权限
            if (request.getRequestURI().startsWith("/api/")) {
                String permission = request.getRequestURI().substring(5).replace("/", ":");
                List<String> userPermissions = sysMenuMapper.getPermissions(ids[1]);
                List<String> allPermissions = sysMenuMapper.getAllPermissions();

                // 1、权限标识不在全部标识集中，则放行
                boolean hasPerm1 = !allPermissions.contains(permission);
                // 2、包含在用户权限集中，则放行
                boolean hasPerm2 = allPermissions.contains(permission) && userPermissions.contains(permission);

                return hasPerm1 || hasPerm2;
            }

            // 其他情况返回 无权访问消息
            throw new AppException(SysErrorEnum.R403);
        }

        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}
